package cn.itcast.nems.manager.kc;

import cn.itcast.nems.manager.kc.model.KeycloakBean;
import cn.itcast.nems.manager.redis.RedisUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ResponseStatusException;

import java.util.Objects;

/**
 * KeyCloak相关调用service
 * 
 * @author JiangYanyu
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeyCloakManager {
    private final static Logger log = LoggerFactory.getLogger(KeyCloakManager.class) ;
    /**
     * spring 模板请求类
     */
    private RestTemplate restTemplate;
    /**
     * redis缓存工具类
     */
    private RedisUtil redisUtil;
    /**
     * 配置参数
     */
    private KeycloakBean keycloakBean ;
    /**
     * 合同系统KeyCloak AccessToken缓存前缀
     */
    public static final String EMSV2_KEYCLOAK_ACCESS_TOKEN = "EMSV2_KEYCLOAK_ACCESS_TOKEN:";

    /**
     * 校验token是否有效
     *
     * @param token
     *            要校验的token
     * @param clientId
     *            应用id
     * @return true 有效
     */
    public boolean handleValidToken(String token, String clientId) {
        if (!StringUtils.hasText(token) || !StringUtils.hasText(clientId)) {
            // 参数缺失
            return false;
        }
        // 请求KeyCloak通用token
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("client_id", keycloakBean.getKeyCloakClientId());
        paramMap.add("client_secret", keycloakBean.getKeyCloakClientSecret());
        paramMap.add("token", token);
        log.info("handleValidToken 请求参数：{}", paramMap);
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(paramMap, requestHeaders);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(
                keycloakBean.getKeyCloakBaseUrl() + "/realms/" + keycloakBean.getKeyCloakRealm() + "/protocol/openid-connect/token/introspect", requestEntity,
            String.class);
        log.info("handleValidToken 响应：{}", responseEntity);

        // 解析响应
        JSONObject jsonObject = parseKeyCloakResponse(responseEntity);
        return jsonObject.getBoolean("active");
    }

    /**
     * 获取KeyCloak 应用AccessToken
     *
     * @return KeyCloak 应用AccessToken
     */
    public String getKeyCloakAccessToken() throws Exception {

        // 从缓存中获取KeyCloak AccessToken
        String accessToken = (String)redisUtil.get(EMSV2_KEYCLOAK_ACCESS_TOKEN + keycloakBean.getKeyCloakClientId());
        if (StringUtils.hasText(accessToken)) {
            // 有则返回
            return accessToken;
        }
        // 请求KeyCloak通用token
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("client_id", keycloakBean.getKeyCloakClientId());
        paramMap.add("client_secret", keycloakBean.getKeyCloakClientSecret());
        paramMap.add("grant_type", "client_credentials");
        log.info("getKeyCloakAccessToken 请求参数：{}", paramMap);
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(paramMap, requestHeaders);
        ResponseEntity<String> responseEntity =
            restTemplate.postForEntity(keycloakBean.getKeyCloakBaseUrl() + "/realms/" + keycloakBean.getKeyCloakRealm() + "/protocol/openid-connect/token",
                requestEntity, String.class);
        log.info("getKeyCloakAccessToken 响应：{}", responseEntity);

        // 解析响应
        JSONObject jsonObject = parseKeyCloakResponse(responseEntity);
        accessToken = jsonObject.getString("access_token");
        // KeyCloak通用token 超时时间（秒）
        int expiresIn = jsonObject.getInteger("expires_in");
        // 缓存 KeyCloak通用token
        redisUtil.set(EMSV2_KEYCLOAK_ACCESS_TOKEN + keycloakBean.getKeyCloakClientId(), accessToken,
            (expiresIn > 500 ? expiresIn - 500L : expiresIn));
        return accessToken;
    }

    /**
     * 解析KeyCloak响应信息
     *
     * @param response
     *            响应
     * @return 响应信息
     */
    private JSONObject parseKeyCloakResponse(ResponseEntity<String> response) {
        final String body = response.getBody();
        JSONObject result = JSON.parseObject(body);
        if (!response.getStatusCode().is2xxSuccessful() && Objects.nonNull(result)) {
            String errorDescription = result.getString("error_description");
            log.error("解析KeyCloak响应信息：错误信息为：{}", errorDescription);
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "请求错误，请关闭窗口稍后重试");
        }
        return result;
    }
}
